package de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Overlays accuracy range images.
 * 
 * @author Eva Peters
 *
 */
public class AccuracyRangeArrayImageOverlay extends AbstractImage
{
	/**
	 * The accuracy range images that should be overlayed.
	 */
	private List<RangeArrayImage> accuracyRangeImages;
	
	/**
	 * The expected width.
	 */
	private int width = 0;
	
	/**
	 * The expected height.
	 */
	private int height = 0;
	
	/**
	 * Constructor.
	 */
	public AccuracyRangeArrayImageOverlay()
	{
		this.accuracyRangeImages = new ArrayList<RangeArrayImage>();
	}
	
	/**
	 * Adds the given accuracy range image to the existing list of accuracy range images.
	 * 
	 * @param accuracyRangeImage The accuracy range image.
	 * 
	 * @throws IncompatibleImagesException If the image is incompatible with the existing ones.
	 */
	public void addAccuracyRangeImage(RangeArrayImage accuracyRangeImage) throws IncompatibleImagesException
	{
		String incompatibleMessage = "This accuracy range image cannot be overlay to determine the accuracy because ";
		
		int currentWidth = accuracyRangeImage.getWidth();
		int currentHeight = accuracyRangeImage.getHeight();
		if(width == 0 && height == 0)
		{
			width = currentWidth;
			height = currentHeight;
			
			this.arrayBitmap = new ArrayBitmap(width, height);
		}
		else
		{
			if(width != currentWidth)
			{
				throw new IncompatibleImagesException(incompatibleMessage + "the widths are different ('" + width + "' and '" + currentWidth + "').");
			}
			if(height != currentHeight)
			{
				throw new IncompatibleImagesException(incompatibleMessage + "the heights are different ('" + height + "' and '" + currentHeight + "').");
			}
			
			accuracyRangeImages.add(accuracyRangeImage);
		}
	}
	
	/**
	 * Overlays all images.
	 * 
	 * Mark only these pixels that are marked in all accuracy range images.
	 * Computes the maximum distance (accuracy) between the given dongle position and the marked pixels.
	 * 
	 * @param position The computed position of the dongle.
	 * @return The accuracy.
	 */
	public double overlayAndDetermineAccuracy(PixelPosition position)
	{
		double maximumDistance = 0; //Initialize accuracy
		
		for(int y = 0; y < height; y++)
		{
			for(int x = 0; x < width; x++)
			{
				boolean mark = true; //Initialize
				for(RangeArrayImage accuracyRangeImage : accuracyRangeImages)
				{
					if(! accuracyRangeImage.isPixelMarked(x, y))
					{
						mark = false;
						break;
					}
				}
				//still true
				if(mark)
				{
					markPixel(x, y);
					//Accuracy
					double distance = position.computeDistance(x, y);
					if(distance > maximumDistance)
					{
						maximumDistance = distance;
					}
				}
			}
		}
		return maximumDistance;
	}

	/**
	 * Returns the positions of the marked pixels in the overlay.
	 * 
	 * @return The positions of the marked pixels
	 */
	public List<PixelPosition> getPositionsOfMarkedPixels()
	{
		List<PixelPosition> positions = new ArrayList<PixelPosition>();
		
		for(int y = 0; y < height; y++)
		{
			for(int x = 0; x < width; x++)
			{
				boolean marked = isPixelMarked(x, y);
				if(marked)
				{
					positions.add(new PixelPosition((double) x, (double) y));
				}
			}
		}
		
		return positions;
	}
}
